﻿// <copyright file="PlayerDisplay.xaml.cs" company="NorthCoast NERO">
//     NorthCoast NERO. All rights reserved.
// </copyright>
// <author>Eric Kociecki</author>
namespace NcnPlayerCheckIn.Controls
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    /// <summary>
    /// Interaction logic for PlayerDisplay.xaml
    /// </summary>
    public partial class PlayerDisplay : UserControl, INotifyPropertyChanged
    {
        /// <summary>
        /// This is the variable behind the IsAlternate property.  It should only be accessed through the property.
        /// </summary>
        private bool isAlternate = false;

        /// <summary>
        /// The player object for this display control.  Data from this Player object should usually be accessed through the corresponding Properties of this PlayerDisplay class.
        /// </summary>
        private Player player = new Player();

        /// <summary>
        /// A custom control that will be in the discountSelectionPopper Popup control.  It allows the user to select applicable discounts for the player.
        /// </summary>
        private DiscountSelection discountSelector = new DiscountSelection();

        /// <summary>
        /// A popup to hold the discountSelector control.
        /// </summary>
        private Popup discountSelectionPopper = new Popup();

        /// <summary>
        /// Initializes a new instance of the PlayerDisplay class.
        /// </summary>
        public PlayerDisplay()
        {
            this.InitializeComponent();

            this.DataContext = this;

            List<string> chapters = new List<string>(new string[] { "NCN", "ARGO", "Boston", "Cinci", "Elkins", "Indiana", "Metro DC", "Metro Detroit", "Piedmont", "PRO", "SWV", "VALOR", "WAR", "Other (see notes)" });
            this.homeChapterInput.ItemsSource = chapters;
            this.HomeChapter = chapters[0];

            // sets up discount selection
            this.discountSelector.ManagerControl = this;
            this.discountSelectionPopper.Child = this.discountSelector;

            this.IsAlternate = false;
        }

        /// <summary>
        /// This event indicates that a property has changed.  It is used for data binding to UI elements.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets or sets the player object for this display control.  Data from this Player object should usually be accessed through the corresponding Properties of this PlayerDisplay class.
        /// </summary>
        public Player Player
        {
            get
            {
                return this.player;
            }

            set
            {
                if (value.IsValid())
                {
                    this.player = value;
                    this.checkOutButton.IsEnabled = true;
                    this.HasCheckedOut = this.HasCheckedOut; // this adds the strikethrough if necessary
                    this.discountSelector.SelectedDiscounts = this.Player.Discounts; // this updates the discountSelector window with player's discounts
                    this.NotifyPropertyChanged("Player");
                    this.NotifyPropertyChanged("PlayerName");
                }
            }
        }

        /// <summary>
        /// Gets or sets the player's real name.
        /// </summary>
        public string PlayerName
        {
            get
            {
                return this.Player.PlayerName;
            }

            set
            {
                this.Player.PlayerName = value;
                this.NotifyPropertyChanged("PlayerName");
            }
        }

        /// <summary>
        /// Gets or sets the player's character name.
        /// </summary>
        public string CharacterName
        {
            get
            {
                return this.Player.CharacterName;
            }

            set
            {
                this.Player.CharacterName = value;
                this.NotifyPropertyChanged("CharacterName");
            }
        }

        /// <summary>
        /// Gets or sets the player's home chapter.  TODO, consider changing this to an enum or array of known (but editable) chapters.
        /// </summary>
        public string HomeChapter
        {
            get
            {
                return this.Player.HomeChapter;
            }

            set
            {
                this.Player.HomeChapter = value;
                this.NotifyPropertyChanged("HomeChapter");
            }
        }

        /// <summary>
        /// Gets or sets the player's total build points.
        /// </summary>
        public int BuildTotal
        {
            get
            {
                return this.Player.BuildTotal;
            }

            set
            {
                this.Player.BuildTotal = value;
                this.NotifyPropertyChanged("BuildTotal");
                this.NotifyPropertyChanged("CharacterLevel");
            }
        }

        /// <summary>
        /// Gets the character's level.  This is calculated from the build total.
        /// </summary>
        public int CharacterLevel
        {
            get
            {
                return (this.BuildTotal > 0) ? (int)Math.Ceiling((this.BuildTotal - 14) / 10.0) : 0;
            }
        }

        /// <summary>
        /// Gets or sets the number of days the player will be PC'ing.
        /// </summary>
        public int PcDaysCount
        {
            get
            {
                return this.Player.PcDaysCount;
            }

            set
            {
                this.Player.PcDaysCount = value;
                this.NotifyPropertyChanged("PcDaysCount");
                this.NotifyPropertyChanged("EventFee");
            }
        }

        /// <summary>
        /// Gets or sets the number of days the player will be NPCing.
        /// </summary>
        public int NpcDaysCount
        {
            get
            {
                return this.Player.NpcDaysCount;
            }

            set
            {
                this.Player.NpcDaysCount = value;
                this.NotifyPropertyChanged("NpcDaysCount");
                this.NotifyPropertyChanged("EventFee");
            }
        }

        /// <summary>
        /// Gets the cost to this player for the event.  This is calculated based on the number of PC and NPC days, any relevant discounts, and any used game-day credits.
        /// </summary>
        public int EventFee
        {
            get
            {
                double cost = (this.PcDaysCount * Costs.GameDayCostPC) + ((this.PcDaysCount > 0) ? Costs.NonPreRegPenalty : 0);
                int eventFee = (int)Math.Round(cost - this.DiscountValue);
                return (eventFee < 0) ? 0 : eventFee;
            }
        }

        /// <summary>
        /// Gets any relevant discounts for this player.
        /// </summary>
        public Collection<DiscountType> Discounts
        {
            get
            {
                return this.Player.Discounts;
            }
        }

        /// <summary>
        /// Gets any relevant discounts for this player.
        /// </summary>
        public double DiscountValue
        {
            get
            {
                return this.Discounts.Sum(DiscountType => DiscountType.Value); // TODO TEST THIS!!!  never used LINQ before
            }
        }

        /// <summary>
        /// Gets any relevant discounts for this player.
        /// </summary>
        public string DiscountText
        {
            get
            {
                string summary = string.Empty;

                foreach (DiscountType discountType in this.Discounts)
                {
                    if (!string.IsNullOrWhiteSpace(summary))
                    {
                        summary += ", ";
                    }

                    summary += discountType.ToString();
                }

                return summary;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the player has checked out from the event.
        /// </summary>
        public bool HasCheckedOut
        {
            get
            {
                return this.Player.HasCheckedOut;
            }

            set
            {
                this.Player.HasCheckedOut = value;
                if (this.Player.HasCheckedOut)
                {
                    this.checkOutButtonText.Text = "Check In";
                    this.playerNameView.TextDecorations = TextDecorations.Strikethrough;
                    this.characterNameView.TextDecorations = TextDecorations.Strikethrough;
                }
                else
                {
                    this.checkOutButtonText.Text = "Check Out";
                    this.playerNameView.TextDecorations = null;
                    this.characterNameView.TextDecorations = null;
                }

                this.NotifyPropertyChanged("HasCheckedOut");
            }
        }

        /// <summary>
        /// Gets or sets any notes pertaining to the player that are not covered elsewhere.
        /// </summary>
        public string Notes
        {
            get
            {
                return this.Player.Notes;
            }

            set
            {
                this.Player.Notes = value;
                this.NotifyPropertyChanged("Notes");
            }
        }

        /// <summary>
        /// Gets or sets an object reference back to the parent window of this PlayerDisplay object.  This is useful for editing the PlayerList data based on user changes within this control.
        /// </summary>
        public PlayerListPanel ManagerControl { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this PlayerDisplay is an alternating row.  This allows for alternating rows to have a slight difference in background coloring.
        /// </summary>
        public bool IsAlternate
        {
            get
            {
                return this.isAlternate;
            }

            set
            {
                this.isAlternate = value;

                if (this.isAlternate)
                {
                    this.viewPanel.Background = Brushes.LightGray;
                }
                else
                {
                    this.viewPanel.Background = Brushes.White;
                }
            }
        }

        /// <summary>
        /// Puts this object in edit mode.  This will also force any other PlayerDisplay objects belonging to the same manager control to exit edit mode.
        /// </summary>
        public void EnterEditMode()
        {
            this.ManagerControl.ExitEditMode(this);

            this.viewPanel.Visibility = System.Windows.Visibility.Collapsed;
            this.inputPanel.Visibility = System.Windows.Visibility.Visible;

            this.Height = 50;

            this.playerNameInput.Focus();
        }

        /// <summary>
        /// Exits edit mode for this object.
        /// </summary>
        public void ExitEditMode()
        {
            if (this.Player.IsValid())
            {
                // if this is a new player, add it to the PlayerList maintained by MainWindow
                if (!this.ManagerControl.PlayerList.Contains(this.Player))
                {
                    this.ManagerControl.PlayerList.Add(this.Player);
                    this.ManagerControl.UpdatePlayerDisplay();
                }

                this.checkOutButton.IsEnabled = true;
            }
            else
            {
                // clear the invalid data  TODO consider highlighting the bad data or offering to fix it in some way
                this.Player.Clear();
                this.Player = this.Player;  // this refreshes the data binding
                this.checkOutButton.IsEnabled = false;
            }

            this.viewPanel.Visibility = System.Windows.Visibility.Visible;
            this.inputPanel.Visibility = System.Windows.Visibility.Collapsed;

            this.Height = 30;
        }

        /// <summary>
        /// Notifies event triggers of multiple property changes all related to the Discounts property.
        /// </summary>
        public void NotifyDiscountPropertiesChanged()
        {
            this.NotifyPropertyChanged("Discounts");
            this.NotifyPropertyChanged("DiscountText");
            this.NotifyPropertyChanged("DiscountValue");
            this.NotifyPropertyChanged("EventFee");
        }

        /// <summary>
        /// Fires the correct PropertyChanged event based on the given property name.
        /// </summary>
        /// <param name="info">The name of the property that has changed.</param>
        protected void NotifyPropertyChanged(string info)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        /// <summary>
        /// EditButton Click event handler.  This will put the display panel in edit mode.
        /// </summary>
        /// <param name="sender">The control that fired the event.</param>
        /// <param name="e">EventArgs related to the event.</param>
        private void EditButton_Click(object sender, RoutedEventArgs e)
        {
            this.EnterEditMode();
        }

        /// <summary>
        /// Event handler for completedButton Click event.  This will store the entered data and put the display panel back in display-only mode.
        /// </summary>
        /// <param name="sender">The control that fired the event.</param>
        /// <param name="e">EventArgs related to the event.</param>
        private void CompletedButton_Click(object sender, RoutedEventArgs e)
        {
            this.ExitEditMode();
        }

        /// <summary>
        /// Event handler for deletButton Click event.  This will delete the corresponding player entry (which means the Player from the current instance.
        /// </summary>
        /// <param name="sender">The control that fired the event.</param>
        /// <param name="e">EventArgs related to the event.</param>
        private void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            this.ManagerControl.PlayerList.Remove(this.Player);
            this.ManagerControl.UpdatePlayerDisplay();
        }

        /// <summary>
        /// Event handler for discountInputButton Click event.  This will open or close the discountSelection popup.
        /// </summary>
        /// <param name="sender">The control that fired the event.</param>
        /// <param name="e">EventArgs related to the event.</param>
        private void DiscountInputButton_Click(object sender, RoutedEventArgs e)
        {
            if (this.discountSelectionPopper.IsOpen)
            {
                this.discountInputLabel.SetBinding(TextBox.TextProperty, this.discountInputLabel.Tag as Binding);
            }
            else
            {
                this.discountSelectionPopper.PlacementTarget = (UIElement)sender;
                this.discountInputLabel.Tag = this.discountInputLabel.GetBindingExpression(TextBox.TextProperty).ParentBinding;
                this.discountInputLabel.Text = "Done";
            }

            this.discountSelectionPopper.IsOpen = !this.discountSelectionPopper.IsOpen;
        }

        /// <summary>
        /// Event handler for inputField GotFocus event.  This selects all of the text in this field when it gets focus.
        /// </summary>
        /// <param name="sender">The control that fired the event.</param>
        /// <param name="e">EventArgs related to the event.</param>
        private void InputField_GotFocus(object sender, RoutedEventArgs e)
        {
            ((TextBox)sender).SelectAll();
        }
    }
}
